import * as fs from "fs"
import * as path from "path"
import * as crypto from "crypto"
import { v4 as uuidv4 } from "uuid"
import initSqlJs, { Database } from "sql.js"
const Registry = require("winreg")

export class MachineIdResetter {
  private storagePath: string
  private sqlitePath: string
  private machineIdPath: string

  constructor(storagePath: string, sqlitePath: string, machineIdPath: string) {
    this.storagePath = storagePath
    this.sqlitePath = sqlitePath
    this.machineIdPath = machineIdPath
  }

  /**
   * 主入口：重置所有机器ID
   */
  async resetMachineIds(): Promise<boolean> {
    try {
      console.log("=== MachineIdResetter.resetMachineIds 开始 ===")
      console.log("storagePath:", this.storagePath)
      console.log("sqlitePath:", this.sqlitePath)
      console.log("machineIdPath:", this.machineIdPath)

      // 1. 读取现有配置文件
      console.log("步骤1: 读取配置文件...")
      if (!fs.existsSync(this.storagePath)) {
        throw new Error(`配置文件不存在: ${this.storagePath}`)
      }
      const config = JSON.parse(fs.readFileSync(this.storagePath, "utf8"))
      console.log("配置文件读取成功")

      // 2. 生成新的机器ID
      console.log("步骤2: 生成新机器标识...")
      const newIds = await this.generateNewIds()
      console.log("新ID生成完成:", Object.keys(newIds))

      // 3. 更新JSON配置文件
      console.log("步骤3: 更新JSON配置...")
      Object.assign(config, newIds)
      this.forceWriteJsonConfig(config)

      // 4. 更新SQLite数据库（暂时跳过）
      console.log("步骤4: 跳过SQLite数据库...")
      try {
        await this.updateSqliteDb(newIds)
      } catch (err) {
        console.error("SQLite更新失败，跳过:", err)
      }

      // 5. 更新Windows系统ID（暂时跳过）
      console.log("步骤5: 跳过Windows系统ID...")
      try {
        await this.updateSystemIds()
      } catch (err) {
        console.error("系统ID更新失败，跳过:", err)
      }

      console.log("=== 机器ID重置完成！===")
      return true
    } catch (error) {
      console.error("=== 重置机器ID失败 ===")
      console.error("错误详情:", error)
      throw error
    }
  }

  /**
   * 生成新的机器标识ID
   */
  private async generateNewIds(): Promise<Record<string, string>> {
    // 生成新的设备UUID
    const devDeviceId = uuidv4()

    // 生成新的机器ID（SHA256）
    const randomBytes = crypto.randomBytes(32)
    const machineId = crypto.createHash("sha256").update(randomBytes).digest("hex")

    // 生成新的Mac机器ID（SHA512）
    const randomBytes512 = crypto.randomBytes(64)
    const macMachineId = crypto.createHash("sha512").update(randomBytes512).digest("hex")

    // 生成新的SQM ID
    const sqmId = `{${uuidv4().toUpperCase()}}`

    // 更新本地机器ID文件
    this.updateMachineIdFile(devDeviceId)

    return {
      "telemetry.devDeviceId": devDeviceId,
      "telemetry.macMachineId": macMachineId,
      "telemetry.machineId": machineId,
      "telemetry.sqmId": sqmId,
      "storage.serviceMachineId": devDeviceId
    }
  }

  /**
   * 强制写入JSON配置文件
   */
  private forceWriteJsonConfig(config: any): void {
    // 检查文件是否存在并处理只读属性
    if (fs.existsSync(this.storagePath)) {
      const stats = fs.statSync(this.storagePath)
      if (!(stats.mode & 0o200)) {
        console.log("检测到只读文件，正在移除只读属性...")
        fs.chmodSync(this.storagePath, 0o666)
        console.log("只读属性已移除")
      }
    }

    // 写入文件
    fs.writeFileSync(this.storagePath, JSON.stringify(config, null, 2), "utf8")
    console.log("JSON配置文件写入成功")
  }

  /**
   * 更新本地机器ID文件
   */
  private updateMachineIdFile(machineId: string): void {
    // 创建目录（如果不存在）
    const dir = path.dirname(this.machineIdPath)
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true })
    }

    // 强制写入机器ID文件
    this.forceWriteMachineIdFile(machineId)
  }

  /**
   * 强制写入机器ID文件
   */
  private forceWriteMachineIdFile(machineId: string): void {
    // 检查文件是否存在并处理只读属性
    if (fs.existsSync(this.machineIdPath)) {
      const stats = fs.statSync(this.machineIdPath)
      if (!(stats.mode & 0o200)) {
        console.log("检测到只读机器ID文件，正在移除只读属性...")
        fs.chmodSync(this.machineIdPath, 0o666)
        console.log("机器ID文件只读属性已移除")
      }
    }

    // 写入新的机器ID到文件
    fs.writeFileSync(this.machineIdPath, machineId, "utf8")
    console.log("机器ID文件写入成功")
  }

  /**
   * 更新SQLite数据库
   */
  private async updateSqliteDb(newIds: Record<string, string>): Promise<void> {
    console.log("正在更新SQLite数据库")

    // 初始化sql.js，指定wasm文件位置
    const SQL = await initSqlJs({
      locateFile: (file: string) => {
        // 直接从 node_modules 构建路径
        const wasmPath = path.join(__dirname, '..', 'node_modules', 'sql.js', 'dist', file)
        console.log("WASM文件路径:", wasmPath)
        return wasmPath
      }
    })
    
    // 读取现有数据库
    console.log("读取数据库文件:", this.sqlitePath)
    const dbBuffer = fs.readFileSync(this.sqlitePath)
    const db = new SQL.Database(dbBuffer)

    // 创建ItemTable表（如果不存在）
    db.run(`
      CREATE TABLE IF NOT EXISTS ItemTable (
        key TEXT PRIMARY KEY,
        value TEXT
      )
    `)

    // 批量更新所有键值对
    for (const [key, value] of Object.entries(newIds)) {
      db.run("INSERT OR REPLACE INTO ItemTable (key, value) VALUES (?, ?)", [key, value])
      console.log(`更新键值对 ${key} ${value}`)
    }

    // 保存数据库
    const data = db.export()
    fs.writeFileSync(this.sqlitePath, Buffer.from(data))
    
    db.close()
    console.log("SQLite数据库更新成功")
  }

  /**
   * 更新Windows系统级别的ID
   */
  private async updateSystemIds(): Promise<void> {
    console.log("更新系统ID")

    // 更新Windows机器GUID
    await this.updateWindowsMachineGuid()

    // 更新Windows机器ID
    await this.updateWindowsMachineId()

    console.log("系统ID更新成功")
  }

  /**
   * 更新Windows注册表中的MachineGuid
   */
  private async updateWindowsMachineGuid(): Promise<void> {
    return new Promise((resolve, reject) => {
      const regKey = new Registry({
        hive: Registry.HKLM,
        key: "\\SOFTWARE\\Microsoft\\Cryptography"
      })

      const newGuid = uuidv4()
      regKey.set("MachineGuid", Registry.REG_SZ, newGuid, (err: any) => {
        if (err) {
          console.error("更新Windows机器GUID失败:", err)
          reject(err)
        } else {
          console.log("Windows机器GUID更新成功")
          resolve()
        }
      })
    })
  }

  /**
   * 更新Windows注册表中的SQMClient MachineId
   */
  private async updateWindowsMachineId(): Promise<void> {
    return new Promise((resolve, reject) => {
      const newGuid = `{${uuidv4().toUpperCase()}}`
      console.log("新GUID:", newGuid)

      const regKey = new Registry({
        hive: Registry.HKLM,
        key: "\\SOFTWARE\\Microsoft\\SQMClient"
      })

      regKey.set("MachineId", Registry.REG_SZ, newGuid, (err: any) => {
        if (err) {
          console.error("更新Windows机器ID失败:", err)
          reject(err)
        } else {
          console.log("Windows机器ID更新成功")
          resolve()
        }
      })
    })
  }
}
